package damas.logicaYcontroladores;

import damas.modelo.DamasEstado;
import damas.modelo.DamasModelo;
import damas.utils.Coordenadas;
import framework.AdversarySearchEngine;
import javax.swing.SwingWorker;
import utils.Pair;

/**
 * Clase encargada de administrar la l�gica del juego
 *
 * @author Sim�n Emmanuel Guti�rrez Brida
 * @version 0.1
 * <hr>
 * M�todos a implementar
 * @see DamasLogica#verificarMovidaEnEstado(utils.Pair, utils.Pair)
 */
public class DamasLogica
{
    private DamasModelo modelo;
    private AdversarySearchEngine<DamasEstado> ia;
    final int delay = 1000;
    private int turno;
    private boolean jugando;

    /**
     * Constructor de la clase
     * @param modelo : el modelo del juego, contiene tambi�n el estado utilizado en el problema de b�squeda : {@code DamasModelo}
     * @param ia : el motor de b�squeda para poder obtener el siguiente movimiento : {@code AdversarySearchEngine<DamasEstado> ia}
     */
    public DamasLogica(DamasModelo modelo, AdversarySearchEngine<DamasEstado> ia)
    {
        this.modelo = modelo;
        this.ia = ia;
        this.turno = 0;
        this.jugando = false;
    }

    /**
     * Inicializa la gui y calcula el siguiente movimiento por si el jugador 1 es ia
     */
    private void iniciarJuego()
    {
        this.jugando = true;
        this.modelo.startGUI();
        siguienteMovimiento();
    }

    /**
     * Inicia una nueva partida
     */
    public void nuevoJuego()
    {
        //define un estado nuevo y deshabilita las preferencias
        this.turno = 0;
        this.modelo.reset();
        this.modelo.definirEstado(new DamasEstado(this));
        this.modelo.deshabilitarPreferencias();
        iniciarJuego();
    }

    /**
     * Mueva una ficha desde su posici�n original hasta el destino
     * @param inicio : la posici�n de la ficha a mover : {@code Pair<Integer,Integer>}
     * @param destino : la posici�n a la cual se mueve : {@code Pair<Integer,Integer>}
     * <hr>
     * solo se cambia de turno si no es posible seguir comiendo con la misma ficha
     */
    public void mover(Pair<Integer, Integer> inicio, Pair<Integer, Integer> destino)
    {
        if (movidaValida(inicio, destino))
        {
            boolean continua = this.modelo.mover(inicio, destino);
            this.modelo.update();

            if (!continua)
                turno = 1 - turno;

            if (!this.modelo.juegoTermino())
                siguienteMovimiento();
        }
        else
        {
            this.modelo.update(); //deshace la movida incorrecta
        }
    }

    /**
     * Retorna si un movimiento es v�lido
     * @param inicio : la posici�n de la ficha a mover : {@code Pair<Integer,Integer>}
     * @param destino : la posici�n a la cual se mueve : {@code Pair<Integer,Integer>}
     * @return true sii la movida es v�lida
     * <hr>
     * puntos a tener en cuenta:
     * <li>hay una ficha en la posici�n de inicio</li>
     * <li>el color de la ficha se corresponde al color del jugador actual</li>
     * <li>es una movida v�lida</li>
     * <li>se est� comiendo una ficha de forma correcta</li>
     * <li>comidas consecutivas se hacen con la misma ficha</li>
     */
    public boolean movidaValida(Pair<Integer, Integer> inicio, Pair<Integer, Integer> destino)
    {
        int valorAcomprobar = this.modelo.colorJugador(turno);
        valorAcomprobar *= this.modelo.esDama(inicio) ? DamasModelo.DAMA : 1;
        if (this.modelo.fichaEn(inicio) == valorAcomprobar)
        {
            //solo se verific� si la ficha pertenece al jugador
            return verificarMovidaEnEstado(inicio, destino);
        }
        else
        {
            return false;
        }
    }

    /**
     * Verifica una movida utilizando datos del estado (DamasEstado)
     * @param inicio : la posici�n de la ficha a mover : {@code Pair<Integer,Integer>}
     * @param destino : la posici�n a la cual se mueve : {@code Pair<Integer,Integer>}
     * @return true sii la movida es v�lida
     */
    private boolean verificarMovidaEnEstado(Pair<Integer, Integer> inicio, Pair<Integer, Integer> destino)
    {
        boolean result = true;
        Pair<Integer, Integer> pos = null;
        if (Coordenadas.coordenadaValida(inicio) && Coordenadas.coordenadaValida(destino) && modelo.fichaEn(destino) == 0)
        {
            if (modelo.fichaEn(inicio) != modelo.colorJugador(turno) || modelo.fondoEn(destino) != DamasModelo.FONDO_NEGRO)
                result = false;

            if (modelo.puedeComerYNoLoHace(inicio))
                result = false;

            if (Coordenadas.cercanos(inicio, destino))
            {
                if (!modelo.esDama(inicio))
                {
                    if (modelo.fichaEn(inicio) == DamasModelo.ROJA)
                        result = result && Coordenadas.elMovEsPaArriba(inicio, destino);
                    else //si es negro
                        result = result && Coordenadas.elMovEsPaAbajo(inicio, destino);
                }
            }
            else if (!modelo.puedeComer(inicio))
                result = false;


            result = result && !Coordenadas.isMovLateral(inicio, destino);

            if ((pos = modelo.posicionPostComida(inicio)) != null)// DESDE UNA POSICION PODES LLEGAR A TENER 2 PARA COMER??
            {
                if (!Coordenadas.iguales(pos, destino))
                    result = false;
            }
        }
        else
            result = false;
        return result;
    }

    /**
     * @return el jugador actual no es humano : {@code boolean}
     */
    private boolean juegaIA()
    {
        return !this.modelo.esHumano(this.modelo.colorJugador(turno));
    }

    /**
     * Ejecuta el siguiente movimiento en caso de tratarse de un jugador no humano
     */
    private void siguienteMovimiento()
    {
        boolean soyIA = juegaIA();
        boolean puedoMover = !this.modelo.juegoTermino();
        if (puedoMover)
        {
            if (soyIA)
            {
                AiTask aiTask = new AiTask();
                aiTask.execute();
            }
        }
        else
        {
            if (!this.modelo.juegoTermino())
            {
                cambiarJugadorActual();
                siguienteMovimiento();
            }
        }
    }

    /**
     * Cambia el jugador actual (a quien le toca)
     */
    private void cambiarJugadorActual()
    {
        turno = 1 - turno;
        this.modelo.obtenerEstado().changeLevel();
        this.modelo.update();
    }

    public int colorJugadorActual()
    {
        return this.modelo.colorJugador(turno);
    }

    /**
     * @return true si se est� jugando una partida : {@code boolean}
     */
    public boolean partidaEnCurso()
    {
        return this.jugando;
    }

    /**
     * Detiene la partida
     */
    public void frenarPartida()
    {
        this.jugando = false;
        this.turno = 0;
        this.modelo.deshabilitarTablero();
        this.modelo.habilitarPreferencias();
    }

    public void cambiarNombreJugador(int jugador, String nuevoNombre)
    {
        this.modelo.definirNombre(jugador, nuevoNombre);
    }

    public void cambiarCerebroJugador(int jugador, boolean cerebro)
    {
        this.modelo.definirCerebro(jugador, cerebro);
    }

    public void cambiarColorJugador(int jugador, int nuevoColor)
    {
        this.modelo.definirColor(jugador, nuevoColor);
    }

    public String obtenerNombreJugador(int jugador)
    {
        return this.modelo.nombreJugador(this.modelo.colorJugador(jugador));
    }

    public boolean obtenerCerebroJugador(int jugador)
    {
        return !this.modelo.esHumano(this.modelo.colorJugador(jugador));
    }

    public int obtenerColorJugador(int jugador)
    {
        return this.modelo.colorJugador(jugador);
    }

    public void mostrarGUI()
    {
        this.modelo.mostrarGUI();
    }

    //TAREA QUE CORRE LA CONSULTA EN EL MOTOR
    private class AiTask extends SwingWorker<DamasEstado, Void>
    {
        @Override
        synchronized protected DamasEstado doInBackground()
        {
            modelo.interaccionTablero(false);
            return ia.nextMove(modelo.obtenerEstado());
        }

        @Override
        synchronized public void done()
        {
            try
            {
                wait(delay);
                modelo.definirEstado(get());
                turno = 1 - turno;
                modelo.update();
                if (juegaIA()) siguienteMovimiento();
            }
            catch (InterruptedException ignore) {}
            catch (java.util.concurrent.ExecutionException e)
            {
                String why = null;
                Throwable cause = e.getCause();
                if (cause != null)
                {
                    why = cause.getMessage();
                }
                else
                {
                    why = e.getMessage();
                }
                System.err.println("Error: " + why);
            }
        }

    }
}